import {
  Children,
  cloneElement,
  createElement,
  HTMLAttributes,
  isValidElement,
  ReactNode,
  useState,
  type FC,
} from 'react';
import classNames from 'classnames';
import { useClickAway, useEnsuredForwardedRef, useUpdateEffect } from 'react-use';
import { dropdownDefaultProps } from './defaultProps';
import type { DropdownOptions } from './type';
import DropdownMenu from './DropdownMenu';
import DropdownItem from './DropdownItem';
import DropdownDivider from './DropdownDivider';
import { DropdownContextProvider, useDropdownContext } from './DropdownContext';

export interface DropdownProps extends DropdownOptions, HTMLAttributes<HTMLDivElement> {}

interface DropdownInterface extends FC<DropdownProps> {
  Menu: typeof DropdownMenu;
  Item: typeof DropdownItem;
  Divider: typeof DropdownDivider;
}

const Child: FC<DropdownProps> = (props) => {
  const { className, ui, selection, name, placeholder, fluid, children, forwardedRef, ...dropdownProps } = props;
  const [states, setStates] = useDropdownContext();
  const [active, setActive] = useState(states.opened);
  const [visible, setVisible] = useState(states.opened);
  const [upwardly, setUpwardly] = useState(states.upward);

  const ensuredForwardRef = useEnsuredForwardedRef(forwardedRef!);

  useClickAway(ensuredForwardRef, () => {
    if (states.opened) {
      setStates({ ...states, opened: false });
    }
  });

  useUpdateEffect(() => {
    if (states.opened) {
      setActive(true);
      setTimeout(() => {
        setVisible(true);
      }, states.duration);
    } else {
      setActive(false);
      setVisible(false);
    }
  }, [states.opened]);

  useUpdateEffect(() => {
    setUpwardly(states.upward);
  }, [states.upward]);

  let node: ReactNode;

  if (children && Children.only(children) && isValidElement(children) && children.type === DropdownMenu) {
    node = cloneElement(children, { key: children.key ?? 'dropdownMenu', ...children.props, open: states.opened });
  } else {
    node = children;
  }

  return createElement(
    'div',
    {
      ref: ensuredForwardRef,
      className: classNames({ ui, selection, upward: upwardly, fluid }, 'dropdown', { active, visible }, className),
      tabIndex: 0,
      onClick: () => {
        setStates({ ...states, opened: !states.opened });
      },
      ...dropdownProps,
    },
    [
      selection && (
        <input
          key="input"
          type="hidden"
          name={name}
          defaultValue={typeof states.value === 'boolean' ? (states.value ? 1 : 0) : states.value}
        />
      ),
      placeholder && states.value === undefined ? (
        <div key="placeholder" className="default text">
          {placeholder}
        </div>
      ) : (
        <div key="text" className="text">
          {states.text}
        </div>
      ),
      node,
    ],
  );
};

const Dropdown: DropdownInterface = (props) => {
  const { open, duration, value, text, upward } = props;
  return (
    <DropdownContextProvider
      initialValue={{
        opened: open,
        duration: duration,
        upward: upward,
        defaultUpward: upward,
        value: value,
        text: text,
      }}
    >
      <Child {...props} />
    </DropdownContextProvider>
  );
};

Dropdown.displayName = 'Dropdown';
Dropdown.defaultProps = dropdownDefaultProps;

Dropdown.Menu = DropdownMenu;
Dropdown.Item = DropdownItem;
Dropdown.Divider = DropdownDivider;

export default Dropdown;
